package com.example.tdemo.thread.volatile_demo;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

@Data
class Data1 {
    /**
     * volatile保证可见性
     * 线程修改变量大伙能感知到
     */
    volatile int num = 0;

    AtomicInteger atomicNum = new AtomicInteger(0);

    AtomicStampedReference<AtomicInteger> stampAtomic = new AtomicStampedReference<>(atomicNum, 0);

    public Data1() {

    }

    public void add() {
        // ++ += 非线程安全
        this.num += 1;
    }

    public void addAtomic() {
        this.atomicNum.getAndIncrement();
    }

    public void addStampAtomicABA() {
        stampAtomic.attemptStamp(stampAtomic.getReference(), stampAtomic.getStamp() + 1);
    }
}

@Data
@AllArgsConstructor
class User {
    String name;

    int age;
}


public class VolatileDemo1 {

    // 保证可见性
    // 不保证原子性
    // 禁止指令重排
    public static void main(String[] args) {
//        showAble();

//        noAtomic();

//        atomic();

//        atomicRefrence();

        preventABA();
    }

    // 原子引用
    private static void atomicRefrence() {
        User zhang = new User("zhang", 18);

        User li = new User("li", 29);

        AtomicReference<User> atomicReference = new AtomicReference<>();
        atomicReference.set(zhang);

        System.out.println(atomicReference.compareAndSet(zhang, li) + "\t" + atomicReference.get().toString());

        System.out.println(atomicReference.compareAndSet(zhang, li) + "\t" + atomicReference.get().toString());
    }

    // 预防ABA 问题
    private static void preventABA() {
        User zhang = new User("zhang", 18);

        User li = new User("li", 29);


        AtomicStampedReference<User> atomicStampedReference = new AtomicStampedReference<>(zhang, 1);

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            atomicStampedReference.compareAndSet(zhang, li, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t" + atomicStampedReference.getReference().toString());
            atomicStampedReference.compareAndSet(li, zhang, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t" + atomicStampedReference.getReference().toString());

        }, "t1").start();

        new Thread(() -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int stamp = atomicStampedReference.getStamp();
            atomicStampedReference.compareAndSet(zhang, li, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t" + atomicStampedReference.getReference().toString());
        }, "t2").start();
    }

    // 不保证原子性
    private static void atomic() {
        Data1 data = new Data1();

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    data.addAtomic();
                }
            }).start();
        }

        // 默认2个线程
        // main线程  gc线程
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }

        // main 9351
        System.out.println(Thread.currentThread().getName() + " " + data.atomicNum.get());
    }

    // 不保证原子性
    private static void noAtomic() {
        Data1 data = new Data1();

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    data.add();
                }
            }).start();
        }

        // 默认2个线程
        // main线程  gc线程
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }

        // main 9351
        System.out.println(Thread.currentThread().getName() + " " + data.num);
    }

    // 可见性
    private static void showAble() {
        Data1 data = new Data1();

        new Thread(() -> {
            try {
                Thread.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            data.add();
            System.out.println(Thread.currentThread().getName() + " data.num = " + data.num);
        }).start();
//
        while (data.num == 0) {

        }
        System.out.println(Thread.currentThread().getName() + " data.num = " + data.num);
    }

    // 可见性
    // 有序性
    // 原子性

}
